'''
Created on Oct 23, 2009

@author: Elena Erbiceanu, Greg Tener

Simple test project, trying to evolve a bit-string individual with all bits set to 1.
'''

import random

from GA import GA

class Individual():
	
	' Static variable, defining the length of an individual. All individuals should be the same length. '
	length = 10
	
	def __init__(self):
		self.genes = [0 for i in range(Individual.length)]
		self.fitness = None
		
	def __str__(self):
		string_representation = 'Genes: ['
		for gene in self.genes:
			if (gene == 1):
				string_representation = string_representation + '1'
			else:
				string_representation = string_representation + '0'
		return string_representation + '] Fitness: ' + str(self.fitness)

class Evaluator():
	def __init__(self):
		pass
	
	@staticmethod
	def evaluate(dude):
		dude.fitness = sum(dude.genes)		
			
	
def zeroes_population_initializer(population_size):
	return [Individual() for i in range(population_size)]

def tournament_selector(population):
	parents = []
	
	for i in range(2 * len(population)):
		dude1 = random.choice(population)
		dude2 = random.choice(population)
		
		if dude1.fitness >= dude2.fitness:
			parent = dude1
		else:
			parent = dude2
			
		parents.append(parent)
	
	couples = []
	for i in range(0, len(parents), 2):
		couples.append(tuple([parents[i], parents[i + 1]]))
		
	return couples 


def crossover_swap_single_point(dude1, dude2):
	cut_point = random.randint(0, Individual.length - 1)
	child = Individual()
	child.genes = dude1.genes[:cut_point] + dude2.genes[cut_point:]	
	print('Crossover: {%s} + {%s} Cut:%d --> %s' % (dude1, dude2, cut_point, child))
	return child


def mutate_single_gene(dude):
	child = dude;
	position = random.randint(0, Individual.length - 1)
	child.genes[position] = 1 - child.genes[position]
	print('Mutate:    {%s} Pos:%d --> %s' % (dude, position, child))
	return child

def merge_best(population1, population2):
	merged_population = population1 + population2
	def key(dude):
		return dude.fitness
	sorted_population = list(sorted(merged_population, key = key, reverse = True))
	truncated_population = sorted_population[:len(population1)]
	return truncated_population

def main():
	ga = GA() 
	ga.population_size = 10
	ga.evaluator = Evaluator
	ga.population_initializer = zeroes_population_initializer
	ga.parents_selector = tournament_selector
	ga.crossover = crossover_swap_single_point
	ga.mutate = mutate_single_gene
	ga.merge_offspring = merge_best
	ga.run()
	return 0

if __name__ == '__main__':	
	main()
